package utils

import (
	"errors"
	"fmt"
	"reflect"
	"sync"
)

type eventTask struct {
	f interface{}
	v reflect.Value
	t reflect.Type
	p []reflect.Type
	n int
}

func NewEvent() Event {
	return &event{
		taskMap: make(map[interface{}]*eventTask),
		mu:      sync.RWMutex{},
	}
}

type Event interface {
	On(event interface{}, task interface{}) error
	Fire(event interface{}, params []interface{}) ([]reflect.Value, error)
	FireBackground(event interface{}, params []interface{}) (chan []reflect.Value, error)
	FireInterface(event interface{}, params []interface{}) ([]interface{}, error)
	Clear(event interface{}) error
	ClearEvents()
	HasEvent(event interface{}) bool
	Events() []interface{}
	EventCount() int
}

type event struct {
	taskMap map[interface{}]*eventTask

	mu sync.RWMutex
}

func (e *event) On(event interface{}, f interface{}) error {
	e.mu.Lock()
	// 客户端断开重连，无法清理所有数据
	// if _, ok := e.taskMap[event]; ok {
	// 	e.mu.Unlock()
	// 	return errors.New("event already defined")
	// }
	v := reflect.ValueOf(f)
	t := v.Type()
	if t.Kind() != reflect.Func {
		e.mu.Unlock()
		return errors.New("task is not a function")
	}
	n := t.NumIn()
	p := make([]reflect.Type, n)
	for k, _ := range p {
		p[k] = t.In(k)
	}
	e.taskMap[event] = &eventTask{
		f,
		v,
		t,
		p,
		n,
	}
	e.mu.Unlock()
	//fmt.Println(e.taskMap)
	return nil
}

func (e *event) Fire(event interface{}, params []interface{}) ([]reflect.Value, error) {
	f, in, err := e.read(event, params)
	if err != nil {
		return nil, err
	}
	result := f.Call(in)
	return result, nil
}

func (e *event) FireBackground(event interface{}, params []interface{}) (chan []reflect.Value, error) {
	f, in, err := e.read(event, params)
	if err != nil {
		return nil, err
	}
	results := make(chan []reflect.Value)
	go func() {
		results <- f.Call(in)
	}()
	return results, nil
}

func (e *event) FireInterface(event interface{}, params []interface{}) ([]interface{}, error) {
	f, in, err := e.read(event, params)
	if err != nil {
		return nil, err
	}
	value := f.Call(in)
	vs := make([]interface{}, len(value))
	for i, _ := range value {
		v := value[i]
		switch vi := v.Interface().(type) {
		case bool:
		case byte:
		case int:
		case int64:
		case float32:
		case float64:
		case string:
		case []int:
		case []string:
		case []byte:
		case []bool:
		case error:
			if vi != nil {
				return nil, vi
			}
		default:
			if v.IsNil() {
				vs[i] = nil
			} else {
				vs[i] = vi
			}
			continue
		}
		vs[i] = v.Interface()
	}
	return vs, nil
}

func (e *event) Clear(event interface{}) error {
	e.mu.Lock()
	if _, ok := e.taskMap[event]; !ok {
		e.mu.Unlock()
		return errors.New("event not defined")
	}
	delete(e.taskMap, event)
	e.mu.Unlock()
	return nil
}

func (e *event) ClearEvents() {
	e.mu.Lock()
	e.taskMap = make(map[interface{}]*eventTask)
	e.mu.Unlock()
}

func (e *event) HasEvent(event interface{}) bool {
	e.mu.RLock()
	_, ok := e.taskMap[event]
	e.mu.RUnlock()
	return ok
}

func (e *event) Events() []interface{} {
	e.mu.RLock()
	defer e.mu.RUnlock()
	events := make([]interface{}, len(e.taskMap))
	i := 0
	for event := range e.taskMap {
		events[i] = event
		i++
	}
	return events
}

func (e *event) EventCount() int {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return len(e.taskMap)
}

func (e *event) read(event interface{}, params []interface{}) (reflect.Value, []reflect.Value, error) {
	e.mu.RLock()
	task, ok := e.taskMap[event]
	e.mu.RUnlock()
	if !ok {
		return reflect.Value{}, nil, errors.New("no task found for event")
	}
	variadic := task.t.IsVariadic()
	if !variadic && len(params) != task.n {
		fmt.Println("Param Event Debug=======>")
		fmt.Println(event)
		fmt.Println(len(params), params)
		fmt.Println(task.n)
		fmt.Println(variadic)
		fmt.Println("<========Event Debug End")
		return reflect.Value{}, nil, errors.New("parameter mismatched")
	}
	in := make([]reflect.Value, len(params))
	for k, _ := range params {
		field := task.p[k]
		fv := reflect.ValueOf(params[k])
		if params[k] != nil && field != fv.Type() && (!variadic || k-1 < task.n) {
			switch field {
			case IntSliceType:
				fv = reflect.ValueOf(ToIntSlice(params[k]))
			case StringSliceType:
				fv = reflect.ValueOf(ToStringSlice(params[k]))
			default:
				fv = fv.Convert(field)
			}
		}
		if fv.Kind() == reflect.Invalid {
			fmt.Println("Kind Event Debug=======>")
			fmt.Println(event)
			fmt.Println(len(params), params)
			fmt.Println(k)
			fmt.Println("<========Event Debug End")
		}
		in[k] = fv
	}
	return task.v, in, nil
}
